package frontend.visitor;

import exceptions.VisitInnerException;
import frontend.cehandler.CEHandler;
import frontend.cehandler.compilationerrors.CEType;
import frontend.lexer.token.StrConToken;
import frontend.lexer.token.Token;
import frontend.lexer.token.TokenType;
import frontend.parser.node.CompUnit;
import frontend.parser.node.Node;
import frontend.parser.node.decl.*;
import frontend.parser.node.expnode.*;
import frontend.parser.node.expnode.binaryexpnodes.*;
import frontend.parser.node.stmt.Block;
import frontend.parser.node.stmt.BlockItem;
import frontend.parser.node.stmt.ForStmt;
import frontend.parser.node.stmt.Stmt;
import frontend.visitor.symboltbl.Symbol;
import frontend.visitor.symboltbl.SymbolTbl;
import ir.types.Type;
import ir.types.datatypes.*;
import ir.values.Argument;
import ir.values.BasicBlock;
import ir.values.FunctionIr;
import ir.values.Value;
import ir.values.users.constants.*;
import ir.values.users.instructions.CallInst;
import ir.values.users.instructions.InstOperator;
import ir.values.users.instructions.ZextInst;
import ir.values.users.instructions.alu.*;
import ir.values.users.instructions.jump.ConditionalBrInst;
import ir.values.users.instructions.jump.RetInst;
import ir.values.users.instructions.jump.UnconditionalBrInst;
import ir.values.users.instructions.mem.GetElementPtrInst;
import ir.values.users.instructions.mem.LoadInst;
import ir.values.users.instructions.mem.StoreInst;
import utils.BBList;
import utils.Pair;
import utils.Wrapper;
import utils.funcinterfaces.TernaryFunction;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Function;

public class Visitor {
    final CompUnit compUnit;
    final PrintWriter printWriter;
    final PrintWriter debugWriter;
    final CEHandler ceHandler;
    final SymbolTbl symbolTbl = new SymbolTbl();
    private final IRBuildFactory irBuildFactory;
    private final VisitorSupporter supporter;
    public Visitor(CompUnit compUnit, PrintWriter printWriter, PrintWriter debugWriter, CEHandler ceHandler) {
        this.compUnit = compUnit;
        this.printWriter = printWriter;
        this.debugWriter = debugWriter;
        this.ceHandler = ceHandler;
        this.irBuildFactory = new IRBuildFactory(symbolTbl);
        this.supporter = new VisitorSupporter(this);
    }

    public void analyse() {
        visitCompUnit(compUnit);
        // TODO: debugWriter.println(symbolTbl);
        printWriter.print(irBuildFactory.generateLLVM());
    }

    private boolean duplicateCheck(String ident, Token causedToken, Node causedNode) {
        if (!symbolTbl.duplicateCheck(ident)) {
            ceHandler.visitorError(CEType.b, "duplicate check failed", causedToken, causedNode);
            return false;
        }
        return true;
    }

    /* Symbol处理方法 */
//    private boolean putSymbol(Symbol symbol) {
//        if (!symbolTbl.putSymbol(symbol)) {
//            supporter.error(CEType.b, "Ident already exist in the scope", symbol);
//            return false;
//        }
//        return true;
//    }
//    private<T extends Symbol> void putSymbols(ArrayList<T> symbolList) {
//        symbolList.forEach(this::putSymbol);
//    }
//    private EleSymbol findEleSymbol(String name, Token causedToken, Node causedNode) {
//        EleSymbol eleSymbol = symbolTbl.findEleSymbol(name);
//        if (eleSymbol == null) {
//            supporter.error(CEType.c, "ident not defined",  causedToken, causedNode);
//            return null;
//        }
//        return eleSymbol;
//    }
//
//    private FuncSymbol findFuncSymbol(String name, Token causedToken, Node causedNode) {
//        FuncSymbol funcSymbol = symbolTbl.findFuncSymbol(name);
//        if (funcSymbol == null) {
//            supporter.error(CEType.c, "function not defined",  causedToken, causedNode);
//            return null;
//        }
//        return funcSymbol;
//    }


    /* 检查check，报错 */
    private boolean checkReturned(Block block) {
        DataType returnType = irBuildFactory.getCurFunctionIrReturnType();

        boolean hasReturnStmtAtEnd = false;
        if (block.getBlockItemList().size() > 0) {
            BlockItem lastBlockItem = block.getBlockItemList().get(block.getBlockItemList().size() - 1);
            if (lastBlockItem.getBlockItemType() == BlockItem.BlockItemType.STMT) {
                Stmt stmt = lastBlockItem.getStmt();
                if (stmt.getType() == Stmt.StmtType.RETURN) {
                    hasReturnStmtAtEnd = true;
                }
            }
        }

        if (returnType instanceof VoidType) {
            if (!hasReturnStmtAtEnd) {
                irBuildFactory.buildInst(new RetInst(new VoidConst()));
            }
            return true;
        }
        else {
            if (!hasReturnStmtAtEnd) {
                irBuildFactory.buildInst(new RetInst(new IntegerConst(0)));
                ceHandler.visitorError(CEType.g, "un-void function not returned at its ending", block.getEndRBraceToken(), block);
                return false;
            }
            else {
                return true;
            }
        }
    }

    /** 前置条件：children被visit */
//    private<T extends ExpNode> boolean checkExpNodeType(ExpNode parent, ArrayList<T> children) {
//        if (children.size() == 1) {
//            parent.setDataType(children.get(0).getDataType());
//            return true;
//        }
//        else {
//            DataType curDataType = new DataType(false, DataType.Type.CONST, 0);
//            for (T child: children) {
//                if (!checkOperandType(child)) {
//                    curDataType.setOccurError(true);
//                }
//                if (child.getDataType().isOccurError()) {
//                    curDataType.setOccurError(true);
//                }
//                curDataType.setType(curDataType.getType().meet(child.getDataType().getType()));
//            }
//            parent.setDataType(curDataType);
//            return !curDataType.isOccurError();
//        }
//    }
//    /** 前置条件：child被visit */
//    private boolean checkOperandType(ExpNode expNode) {
//        DataType childDataType = expNode.getDataType();
//        if (childDataType.getType() == DataType.Type.VOID) {
//            supporter.error(CEType.ov, "cannot handle void datatype", expNode);
//            return false;
//        }
//        if (childDataType.getDimension() > 0) {
//            supporter.error(CEType.ov, "cannot handle datatype whose dimension > 0", expNode);
//            return false;
//        }
//        return true;
//    }
//
//    private boolean checkNotConstLVal(LVal lVal) {
//        if (lVal.getDataType().getType() == DataType.Type.CONST) {
//            supporter.error(CEType.h, "cannot modify const LVal", lVal.getIdentToken(), lVal);
//            return false;
//        }
//        if (lVal.getDataType().getDimension() > 0) {
//            supporter.error(CEType.ov, "cannot modify LVal whose dimension > 0", lVal.getIdentToken(), lVal);
//            return false;
//        }
//        return true;
//    }


    /* 语句级 */
    private void visitCompUnit(CompUnit compUnit) {
        // 编译单元    CompUnit → {Decl} {FuncDef} MainFuncDef
        for (Decl decl: compUnit.getDecls()) {
            visitDecl(decl);
        }
        for (FuncDef funcDef: compUnit.getFuncDefs()) {
            visitFuncDef(funcDef);
        }

        MainFuncDef mainFuncDef = compUnit.getMainFuncDef();
        if (mainFuncDef != null) {
            visitMainFuncDef(mainFuncDef);
        }
    }


    private void visitDecl(Decl decl) {
        switch (decl.getDeclType()) {
            case CONST_DECL -> {
                ConstDecl constDecl = decl.getConstDecl();
                for (ConstDef constDef: constDecl.getConstDefList()) {
                    visitConstDef(constDef);
                }
            }
            case VAR_DECL -> {
                VarDecl varDecl = decl.getVarDecl();
                for (VarDef varDef: varDecl.getVarDefList()) {
                    visitVarDef(varDef);
                }
            }
        }
    }

    private void visitConstDef(ConstDef constDef) {
        // 重名检验
        String ident = constDef.getIdentName();
        if (!duplicateCheck(ident, constDef.getIdentToken(), constDef)) return;

        // 生成Symbol初值
        // 获取维数
        int dimension = constDef.getDimension();

        // 获取维数长度
        int[] offsets = new int[dimension];
        ConstExp[] dimLenConstExp = constDef.getDimLenConstExp();
        for (int i = 0; i < dimension; i++) {
            Integer temp = calcConstExp(dimLenConstExp[i]);
            offsets[i] = (temp == null) ? 0 : temp;
        }

        // 获取初值
        int initial_dim0 = 0;
        int[] initial_dim1 = new int[0];
        int[][] initial_dim2 = new int[0][];
        Integer temp;
        ConstInitVal constInitVal = constDef.getConstInitVal();
        switch (dimension) {
            case 0 -> {
                ConstExp valExp_dim0 = constInitVal.getValExp_dim0();
                temp = calcConstExp(valExp_dim0);
                initial_dim0 = (temp == null) ? 0 : temp;
            }
            case 1 -> {
                initial_dim1 = new int[offsets[0]];
                ArrayList<ConstExp> valExp_dim1 = constInitVal.getValExp_dim1();
                for (int i = 0; i < valExp_dim1.size(); i++) {
                    if (i >= initial_dim1.length) break;
                    temp = calcConstExp(valExp_dim1.get(i));
                    initial_dim1[i] = (temp == null) ? 0 : temp;
                }
            }
            case 2 -> {
                initial_dim2 = new int[offsets[0]][offsets[1]];
                ArrayList<ArrayList<ConstExp>> valExp_dim2 = constInitVal.getValExp_dim2();
                for (int i = 0; i < valExp_dim2.size(); i++) {
                    if (i > initial_dim2.length) break;
                    ArrayList<ConstExp> valExp_dim1 = valExp_dim2.get(i);
                    for (int j = 0; j < valExp_dim1.size(); j++) {
                        if (j > initial_dim2[i].length) break;
                        temp = calcConstExp(valExp_dim1.get(j));
                        initial_dim2[i][j] = (temp == null) ? 0 : temp;
                    }
                }
            }
        }

        // 生成Ir
        Value value;
        if (symbolTbl.isGlobalScope()) {
            // 生成全局变量Ir
            // 生成Constant
            Constant constant = null;
            switch (dimension) {
                case 0 -> {
                    constant = new IntegerConst(initial_dim0);
                }
                case 1 -> {
                    ArrayList<Constant> childrenConstant_dim1 = new ArrayList<>();
                    for (int value_dim0: initial_dim1) {
                        childrenConstant_dim1.add(new IntegerConst(value_dim0));
                    }
                    constant = new ArrayConst(childrenConstant_dim1);
                }
                case 2 -> {
                    ArrayList<Constant> childrenConstant_dim2 = new ArrayList<>();
                    for (int[] value_dim1: initial_dim2) {
                        ArrayList<Constant> childrenConstant_dim1 = new ArrayList<>();
                        for (int value_dim0: value_dim1) {
                            childrenConstant_dim1.add(new IntegerConst(value_dim0));
                        }
                        childrenConstant_dim2.add(new ArrayConst(childrenConstant_dim1));
                    }
                    constant = new ArrayConst(childrenConstant_dim2);
                }
            }

            // 生成GlobalVariable
            value = irBuildFactory.buildGlobalVariable(ident, constant, true);
        }
        else {
            // 生成局部变量Ir
            switch (dimension) {
                case 0 -> {
                    value = irBuildFactory.buildAllocaInst(ident, IntegerType.I32);
                    irBuildFactory.buildInst(new StoreInst(new IntegerConst(initial_dim0), value));
                }
                case 1 -> {
                    value = irBuildFactory.buildAllocaInst(ident, new ArrayType(IntegerType.I32, offsets[0]));
                    for (int i = 0; i < initial_dim1.length; i++) {
                        Value pointer = irBuildFactory.tryBuildGetElementPtrInst(value, new IntegerConst(0), new IntegerConst(i));
                        irBuildFactory.buildInst(new StoreInst(new IntegerConst(initial_dim1[i]), pointer));
                    }
                }
                case 2 -> {
                    value = irBuildFactory.buildAllocaInst(ident, new ArrayType(new ArrayType(IntegerType.I32, offsets[1]), offsets[0]));
                    for (int i = 0; i < initial_dim2.length; i++) {
                        for (int j = 0; j < initial_dim2[i].length; j++) {
                            Value pointer = irBuildFactory.tryBuildGetElementPtrInst(value, new IntegerConst(0), new IntegerConst(i), new IntegerConst(j));
                            irBuildFactory.buildInst(new StoreInst(new IntegerConst(initial_dim2[i][j]), pointer));
                        }
                    }
                }
                default -> {
                    throw new VisitInnerException();
                }
            }

        }

        // 加入符号表
        switch (dimension) {
            case 0 -> {
                symbolTbl.putSymbol(new Symbol(value, symbolTbl.isGlobalScope(), true, initial_dim0));
            }
            case 1 -> {
                symbolTbl.putSymbol(new Symbol(value, symbolTbl.isGlobalScope(), true, initial_dim1, offsets[0]));
            }
            case 2 -> {
                symbolTbl.putSymbol(new Symbol(value, symbolTbl.isGlobalScope(), true, initial_dim2, offsets[0], offsets[1]));
            }
        }

    }

    private void visitVarDef(VarDef varDef) {
        // 重名检验
        String ident = varDef.getIdentName();
        if (!duplicateCheck(ident, varDef.getIdentToken(), varDef)) return;

        // 获取维数
        int dimension = varDef.getDimension();

        // 获取维数长度
        int[] offsets = new int[dimension];
        ConstExp[] dimLenConstExp = varDef.getDimLenConstExp();
        for (int i = 0; i < dimension; i++) {
            Integer temp = calcConstExp(dimLenConstExp[i]);
            offsets[i] = (temp == null) ? 0 : temp;
        }

        // 初值
        InitVal initVal = null;
        Exp valExp_dim0 = null;
        ArrayList<Exp> valExp_dim1 = null;
        ArrayList<ArrayList<Exp>> valExp_dim2 = null;
        if (varDef.hasInitVal()) {
            initVal = varDef.getInitVal();
            valExp_dim0 = initVal.getValExp_dim0();
            valExp_dim1 = initVal.getValExp_dim1();
            valExp_dim2 = initVal.getValExp_dim2();
        }

        int initial_dim0 = 0;
        int[] initial_dim1 = new int[0];
        int[][] initial_dim2 = new int[0][];

        // 如果是全局量则获取初值
        if (symbolTbl.isGlobalScope()) {

            // 获取初值
            if (varDef.hasInitVal()) {
                Integer temp;

                switch (dimension) {
                    case 0 -> {
                        temp = calcExp(valExp_dim0);
                        initial_dim0 = (temp == null) ? 0 : temp;
                    }
                    case 1 -> {
                        initial_dim1 = new int[offsets[0]];
                        for (int i = 0; i < valExp_dim1.size(); i++) {
                            if (i >= initial_dim1.length) break;
                            temp = calcExp(valExp_dim1.get(i));
                            initial_dim1[i] = (temp == null) ? 0 : temp;
                        }
                    }
                    case 2 -> {
                        initial_dim2 = new int[offsets[0]][offsets[1]];
                        for (int i = 0; i < valExp_dim2.size(); i++) {
                            if (i > initial_dim2.length) break;
                            ArrayList<Exp> valExp_dim2_children = valExp_dim2.get(i);
                            for (int j = 0; j < valExp_dim2_children.size(); j++) {
                                if (j > initial_dim2[i].length) break;
                                temp = calcExp(valExp_dim2_children.get(j));
                                initial_dim2[i][j] = (temp == null) ? 0 : temp;
                            }
                        }
                    }
                }
            }
            else {
                switch (dimension) {
                    case 0 -> {
                        initial_dim0 = 0;
                    }
                    case 1 -> {
                        initial_dim1 = new int[offsets[0]];
                    }
                    case 2 -> {
                        initial_dim2 = new int[offsets[0]][offsets[1]];
                    }
                }
            }
        }

        // 生成IR
        Value value;
        if (symbolTbl.isGlobalScope()) {
            // 生成全局变量Ir
            // 生成Constant
            Constant constant = null;
            switch (dimension) {
                case 0 -> {
                    constant = new IntegerConst(initial_dim0);
                }
                case 1 -> {
                    if (varDef.hasInitVal()) {
                        ArrayList<Constant> childrenConstant_dim1 = new ArrayList<>();
                        for (int value_dim0: initial_dim1) {
                            childrenConstant_dim1.add(new IntegerConst(value_dim0));
                        }
                        constant = new ArrayConst(childrenConstant_dim1);
                    }
                    else {
                        constant = new Zeroinitializer(new ArrayType(IntegerType.I32, offsets[0]));
                    }
                }
                case 2 -> {
                    if (varDef.hasInitVal()) {
                        ArrayList<Constant> childrenConstant_dim2 = new ArrayList<>();
                        for (int[] value_dim1: initial_dim2) {
                            ArrayList<Constant> childrenConstant_dim1 = new ArrayList<>();
                            for (int value_dim0: value_dim1) {
                                childrenConstant_dim1.add(new IntegerConst(value_dim0));
                            }
                            childrenConstant_dim2.add(new ArrayConst(childrenConstant_dim1));
                        }
                        constant = new ArrayConst(childrenConstant_dim2);
                    }
                    else {
                        constant = new Zeroinitializer(new ArrayType(new ArrayType(IntegerType.I32, offsets[1]), offsets[0]));
                    }
                }
            }

            // 生成GlobalVariable
            value = irBuildFactory.buildGlobalVariable(ident, constant, false);
        }
        else {
            // 生成局部变量Ir
            switch (dimension) {
                case 0 -> {
                    value = irBuildFactory.buildAllocaInst(ident, IntegerType.I32);
                    if (varDef.hasInitVal()) {
                        irBuildFactory.buildInst(new StoreInst(visitExp(valExp_dim0), value));
                    }
                }
                case 1 -> {
                    value = irBuildFactory.buildAllocaInst(ident, new ArrayType(IntegerType.I32, offsets[0]));
                    if (varDef.hasInitVal()) {
                        for (int i = 0; i < valExp_dim1.size(); i++) {
                            Value pointer = irBuildFactory.tryBuildGetElementPtrInst(value, new IntegerConst(0), new IntegerConst(i));
                            irBuildFactory.buildInst(new StoreInst(visitExp(valExp_dim1.get(i)), pointer));
                        }
                    }
                }
                case 2 -> {
                    value = irBuildFactory.buildAllocaInst(ident, new ArrayType(new ArrayType(IntegerType.I32, offsets[1]), offsets[0]));
                    if (varDef.hasInitVal()) {
                        for (int i = 0; i < valExp_dim2.size(); i++) {
                            ArrayList<Exp> valExp_dim2_children = valExp_dim2.get(i);
                            for (int j = 0; j < valExp_dim2_children.size(); j++) {
                                Value pointer = irBuildFactory.tryBuildGetElementPtrInst(value, new IntegerConst(0), new IntegerConst(i), new IntegerConst(j));
                                irBuildFactory.buildInst(new StoreInst(visitExp(valExp_dim2_children.get(j)), pointer));
                            }
                        }
                    }
                }
                default -> {
                    throw new VisitInnerException();
                }
            }
        }

        // 加入符号表
        if (symbolTbl.isGlobalScope()) {
            switch (dimension) {
                case 0 -> {
                    symbolTbl.putSymbol(new Symbol(value, true, false, initial_dim0));
                }
                case 1 -> {
                    symbolTbl.putSymbol(new Symbol(value, true, false, initial_dim1, offsets[0]));
                }
                case 2 -> {
                    symbolTbl.putSymbol(new Symbol(value, true, false, initial_dim2, offsets[0], offsets[1]));
                }
            }
        }
        else {
            symbolTbl.putSymbol(new Symbol(value, false, false, 0));
        }

    }

    private void visitFuncDef(FuncDef funcDef) {
        // 函数定义    FuncDef → FuncType Ident '(' [FuncFParams] ')' Block // b g j

        // 重名检验
        String ident = funcDef.getIdentName();
        if (!symbolTbl.duplicateCheck(ident)) {
            ceHandler.visitorError(CEType.b, "duplicate check failed", funcDef.getIdentToken(), funcDef);
            return;
        }
        ArrayList<FuncFParam> funcFParams = funcDef.getFuncFParamList();
        {
            Set<String> funcFParamNameSet = new HashSet<String>();
            for (FuncFParam funcFParam: funcFParams) {
                if (!funcFParamNameSet.add(funcFParam.getIdentName())) {
                    ceHandler.visitorError(CEType.b, "duplicate check failed", funcFParam.getIdentToken(), funcFParam);
                    return;
                }
            }
        }

        // 生成FunctionIr
        ArrayList<Argument> arguments = new ArrayList<>();
        for (FuncFParam funcFParam: funcFParams) {
            DataType funcFParamDataType = null;
            int dimension = funcFParam.getDimension();
            ConstExp[] dimLenConstExp = funcFParam.getDimLenConstExp();
            switch (dimension) {
                case 0 -> {
                    funcFParamDataType = IntegerType.I32;
                }
                case 1 -> {
                    funcFParamDataType = new PointerType(IntegerType.I32);
                }
                case 2 -> {
                    funcFParamDataType = new PointerType(
                            new ArrayType(IntegerType.I32, calcConstExp(dimLenConstExp[1]))
                    );
                }
            }
            arguments.add(new Argument(funcFParam.getIdentName(), funcFParamDataType));
        }
        FunctionIr functionIr = irBuildFactory.buildFunction(
                ident,
                funcDef.isReturned() ? IntegerType.I32 : VoidType.VOID,
                arguments
        );

        // 加入符号表
        symbolTbl.putFunctionSymbolAndScope(functionIr);

        // 指针化形参
        irBuildFactory.buildFunctionIrPointerArguments();
        symbolTbl.putArgumentSymbols();

        // 访问block节点
        Block block = funcDef.getBlock();
        visitBlock(block, false);
        checkReturned(block);
    }

    private void visitMainFuncDef(MainFuncDef mainFuncDef) {
        FunctionIr functionIr = irBuildFactory.buildFunction(
                "main",
                IntegerType.I32,
                new ArrayList<>()
        );
        symbolTbl.putFunctionSymbolAndScope(functionIr);

        Block block = mainFuncDef.getBlock();
        visitBlock(block, false);
        checkReturned(block);
    }



    private void visitBlock(Block block, boolean shouldPushScope) {
        if (shouldPushScope) {
            symbolTbl.pushInnerScope();
        }
        for (BlockItem blockItem: block.getBlockItemList()) {
            switch (blockItem.getBlockItemType()) {
                case DECL -> {
                    visitDecl(blockItem.getDecl());
                }
                case STMT -> {
                    visitStmt(blockItem.getStmt());
                }
            }
        }
        symbolTbl.popScope();
    }

    private void visitStmt(Stmt stmt) {
        switch (stmt.getType()) {
            case IF -> {
                Stmt.IfInf ifInf = (Stmt.IfInf) stmt.getStmtInf();

                if (ifInf.hasElse()) {
                    // cond
                    BBList<BasicBlock>.BBNode ifBasicBlockNode = irBuildFactory.createBasicBlock();
                    BBList<BasicBlock>.BBNode elseBasicBlockNode = irBuildFactory.createBasicBlock();
                    BBList<BasicBlock>.BBNode endBasicBlockNode = irBuildFactory.createBasicBlock();
                    visitCond(ifInf.getCond(), ifBasicBlockNode.get(), elseBasicBlockNode.get());

                    // if
                    irBuildFactory.switchCurBasicBlock(ifBasicBlockNode);
                    visitStmt(ifInf.getIfStmt());
                    irBuildFactory.buildInst(new UnconditionalBrInst(endBasicBlockNode.get()));

                    // else
                    irBuildFactory.switchCurBasicBlock(elseBasicBlockNode);
                    visitStmt(ifInf.getElseStmt());
                    irBuildFactory.buildInst(new UnconditionalBrInst(endBasicBlockNode.get()));

                    // end
                    irBuildFactory.switchCurBasicBlock(endBasicBlockNode);
                }
                else {
                    // cond
                    BBList<BasicBlock>.BBNode ifBasicBlockNode = irBuildFactory.createBasicBlock();
                    BBList<BasicBlock>.BBNode endBasicBlockNode = irBuildFactory.createBasicBlock();
                    visitCond(ifInf.getCond(), ifBasicBlockNode.get(), endBasicBlockNode.get());

                    // if
                    irBuildFactory.switchCurBasicBlock(ifBasicBlockNode);
                    visitStmt(ifInf.getIfStmt());
                    irBuildFactory.buildInst(new UnconditionalBrInst(endBasicBlockNode.get()));

                    // end
                    irBuildFactory.switchCurBasicBlock(endBasicBlockNode);
                }
            }
            case FOR -> {
                Stmt.ForInf forInf = (Stmt.ForInf) stmt.getStmtInf();

                BBList<BasicBlock>.BBNode condBasicBlockNode = irBuildFactory.createBasicBlock();
                BBList<BasicBlock>.BBNode bodyBasicBlockNode = irBuildFactory.createBasicBlock();
                BBList<BasicBlock>.BBNode endStmtBasicBlockNode = irBuildFactory.createBasicBlock();
                BBList<BasicBlock>.BBNode endBasicBlockNode = irBuildFactory.createBasicBlock();
                irBuildFactory.pushForLoop(condBasicBlockNode, bodyBasicBlockNode, endStmtBasicBlockNode, endBasicBlockNode);

                // startStmt
                if (forInf.hasForStmt1()) {
                    visitForStmt(forInf.getForStmt1());
                }
                irBuildFactory.buildInst(new UnconditionalBrInst(condBasicBlockNode.get()));

                // cond
                irBuildFactory.switchCurBasicBlock(condBasicBlockNode);
                if (forInf.hasCond()) {
                    visitCond(forInf.getCond(), bodyBasicBlockNode.get(), endBasicBlockNode.get());
                }
                else {
                    irBuildFactory.buildInst(new UnconditionalBrInst(bodyBasicBlockNode.get()));
                }

                // body
                irBuildFactory.switchCurBasicBlock(bodyBasicBlockNode);
                visitStmt(forInf.getStmt());
                irBuildFactory.buildInst(new UnconditionalBrInst(endStmtBasicBlockNode.get()));

                // endStmt
                irBuildFactory.switchCurBasicBlock(endStmtBasicBlockNode);
                if (forInf.hasForStmt2()) {
                    visitForStmt(forInf.getForStmt2());
                }
                irBuildFactory.buildInst(new UnconditionalBrInst(condBasicBlockNode.get()));

                // end
                irBuildFactory.switchCurBasicBlock(endBasicBlockNode);
                irBuildFactory.popForLoop();
            }
            case BREAK -> {
                Stmt.BreakInf breakInf = (Stmt.BreakInf) stmt.getStmtInf();

                if (irBuildFactory.isInForLoop()) {
                    BBList<BasicBlock>.BBNode endBasicBlockNode = irBuildFactory.getEndBasicBlockNode();
                    irBuildFactory.buildInst(new UnconditionalBrInst(endBasicBlockNode.get()));
                }
                else {
                    ceHandler.visitorError(CEType.m, "break stmt is not within a for loop", breakInf.getBreakToken(), stmt);
                }
            }
            case CONTINUE -> {
                Stmt.ContinueInf continueInf = (Stmt.ContinueInf) stmt.getStmtInf();

                if (irBuildFactory.isInForLoop()) {
                    BBList<BasicBlock>.BBNode endStmtBasicBlockNode = irBuildFactory.getEndStmtBasicBlockNode();
                    irBuildFactory.buildInst(new UnconditionalBrInst(endStmtBasicBlockNode.get()));
                }
                else {
                    ceHandler.visitorError(CEType.m, "continue stmt is not within a for loop", continueInf.getContinueToken(), stmt);
                }
            }
            case RETURN -> {
                Stmt.ReturnInf returnInf = (Stmt.ReturnInf) stmt.getStmtInf();

                // 获取返回的Value
                boolean hasExp = returnInf.hasExp();
                Value returnedValue = hasExp ? visitExp(returnInf.getExp()) : new VoidConst();

                // 与当前函数返回值进行类型检查
                DataType expectedType = irBuildFactory.getCurFunctionIrReturnType();
                Type givenType = returnedValue.getType();
                if (expectedType instanceof VoidType) {
                    if (!(givenType instanceof VoidType)) {
                        ceHandler.visitorError(CEType.f, "void function returned with a value", returnInf.getReturnToken(), stmt);
                        returnedValue = new VoidConst();
                    }
                }
                else if (expectedType instanceof IntegerType) {
                    if (givenType instanceof VoidType) {
                        ceHandler.visitorError(CEType.ov, "un-void function returned without a value", returnInf.getReturnToken(), stmt);
                        returnedValue = new IntegerConst(0);
                    }
                    else if (!(givenType instanceof IntegerType)) {
                        ceHandler.visitorError(CEType.ov, "function not returned an IntegerType", returnInf.getReturnToken(), stmt);
                        returnedValue = new IntegerConst(0);
                    }
                }

                // 生成Ir
                irBuildFactory.buildInst(new RetInst(returnedValue));
            }
            case PRINTF -> {
                Stmt.PrintfInf printfInf = (Stmt.PrintfInf) stmt.getStmtInf();
                StrConToken strConToken = printfInf.getStrConToken();
                ArrayList<StrConToken.StrConItem> strConItems = strConToken.getStrConItems();
                ArrayList<Exp> expList = printfInf.getExpList();

                ArrayList<Value> expValues = new ArrayList<>();
                for (Exp exp: expList) {
                    Value tempValue = visitExp(exp);
                    if (tempValue == null) {
                        tempValue = new IntegerConst(0);
                    }
                    expValues.add(tempValue);
                }
                final int placeHolderCnt = strConToken.getPlaceHolderCnt();
                if (placeHolderCnt != expList.size()) {
                    ceHandler.visitorError(CEType.l, "the number of \"%d\" doesn't match with the number of exp", strConToken, stmt);
                }
                if (placeHolderCnt > expList.size()) {
                    int diff = placeHolderCnt - expList.size();
                    for (int i = 0; i < diff; i++) {
                        expValues.add(new IntegerConst(0));
                    }
                }

                int curPlaceHolderCnt = 0;
                for (StrConToken.StrConItem strConItem: strConItems) {
                    if (strConItem instanceof StrConToken.StrItem strItem) {
                        String strVal = strItem.getStrVal();
                        for (char c: strVal.toCharArray()) {
                            ArrayList<Value> args = new ArrayList<>();
                            args.add(new IntegerConst(c));
                            irBuildFactory.buildInst(new CallInst(IRBuildFactory.PUT_CHAR, args));
                        }
                    }
                    else if (strConItem instanceof StrConToken.CharItem charItem) {
                        ArrayList<Value> args = new ArrayList<>();
                        args.add(new IntegerConst(charItem.getCharVal()));
                        irBuildFactory.buildInst(new CallInst(IRBuildFactory.PUT_CHAR, args));
                    }
                    else if (strConItem instanceof StrConToken.PlaceHolderItem placeHolderItem) {
                        Value cur = expValues.get(curPlaceHolderCnt++);
                        ArrayList<Value> args = new ArrayList<>();
                        args.add(cur);
                        irBuildFactory.buildInst(new CallInst(IRBuildFactory.PUT_INT, args));
                    }
                }
            }
            case GETINT -> {
                Stmt.GetintInf getintInf = (Stmt.GetintInf) stmt.getStmtInf();

                // 获取左值Value
                LVal lVal = getintInf.getLVal();
                Value left = visitLVal(lVal, true);
                if (left == null) {
                    break;
                }

                // 生成Ir命令
                Value right = irBuildFactory.buildInst(new CallInst(IRBuildFactory.GET_INT, new ArrayList<>()));
                irBuildFactory.buildInst(new StoreInst(right, left));
            }
            case BLOCK -> {
                Stmt.BlockInf blockInf = (Stmt.BlockInf) stmt.getStmtInf();
                visitBlock(blockInf.getBlock(), true);
            }
            case SEMICN -> {
                Stmt.SemicnInf semicnInf = (Stmt.SemicnInf) stmt.getStmtInf();
            }
            case ASIGN -> {
                Stmt.AssignInf assignInf = (Stmt.AssignInf) stmt.getStmtInf();

                // 获取左值和右值Value
                LVal lVal = assignInf.getLVal();
                Exp exp = assignInf.getExp();
                Value left = visitLVal(lVal, true);
                Value right = visitExp(exp);

                if (left == null || right == null) {
                    break;
                }
                if (!(right.getType() instanceof IntegerType)) {
                    ceHandler.visitorError(CEType.ov, "only IntegerType is allowed", exp);
                    break;
                }

                // 生成Ir命令
                irBuildFactory.buildInst(new StoreInst(right, left));
            }
            case EXP -> {
                Stmt.ExpInf expInf = (Stmt.ExpInf) stmt.getStmtInf();
                visitExp(expInf.getExp());
            }
        }
    }



    /* 表达式级 */
    private void visitForStmt(ForStmt forStmt) {
        LVal lVal = forStmt.getlVal();
        Exp exp = forStmt.getExp();
        Value left = visitLVal(lVal, true);
        Value right = visitExp(exp);

        if (left == null || right == null) {
            return;
        }
        if (!(right.getType() instanceof IntegerType)) {
            ceHandler.visitorError(CEType.ov, "only IntegerType is allowed", exp);
            return;
        }

        // 生成Ir命令
        irBuildFactory.buildInst(new StoreInst(right, left));
    }


    private Value visitLVal(LVal lVal, boolean memoryVisit) {
        // 左值表达式   LVal → Ident {'[' Exp ']'} // c k

        boolean occurError = false;
        // 查符号表
        String ident = lVal.getIdentName();
        Symbol symbol = symbolTbl.findSymbol(ident);
        if (symbol == null) {
            ceHandler.visitorError(CEType.c, "ident not defined", lVal.getIdentToken(), lVal);
            return null;
        }
        Value symbolValue = symbol.getValue();

        // 获取维数和下标
        int offsetDimension = lVal.getDimension();
        Exp[] dimOffsetExp = lVal.getDimOffsetExp();
        Value[] dimOffsetValue = new Value[2];
        for (int i = 0; i < offsetDimension; i++) {
            dimOffsetValue[i] = visitExp(dimOffsetExp[i]);
            if (dimOffsetValue[i] == null) {
                occurError = true;
            }
            else if (!(dimOffsetValue[i].getType() instanceof IntegerType)) {
                occurError = true;
                ceHandler.visitorError(CEType.ov, "not IntegerType", dimOffsetExp[i]);
            }
        }
        if (occurError) {
            return null;
        }

        // 维数检验
//        int symbolDimension = symbol.getDimension();
//        if (offsetDimension > symbolDimension) {
//            ceHandler.visitorError(CEType.ov, "too many offsets", lVal.getIdentToken(), lVal);
//            return null;
//        }

        // 生成Ir
        Value resultValue = symbolValue;
        DataType currentBaseType = ((PointerType) resultValue.getType()).getBaseType();
        ArrayList<Value> getElementPtrInstOperands = new ArrayList<>();

        if (currentBaseType instanceof IntegerType integerType) {
            if (offsetDimension > 0) {
                occurError = true;
                ceHandler.visitorError(CEType.ov, "too many offsets", lVal.getIdentToken());
            }
        }
        else if (currentBaseType instanceof PointerType pointerType) {
            // i32**, [n x i32]**
            // dimOffsetValue[0]
            if (offsetDimension > 0) {
                resultValue = irBuildFactory.buildInst(new LoadInst(resultValue));
                currentBaseType = pointerType.getBaseType();
                getElementPtrInstOperands.add(dimOffsetValue[0]);
            }

            // dimOffsetValue[1]
            for (int i = 1; i < offsetDimension; i++) {
                if (currentBaseType instanceof ArrayType arrayType) {
                    getElementPtrInstOperands.add(dimOffsetValue[i]);
                    currentBaseType = arrayType.getBaseType();
                }
                else {
                    occurError = true;
                    ceHandler.visitorError(CEType.ov, "too many offsets", lVal.getIdentToken());
                    break;
                }
            }
        }
        else if (currentBaseType instanceof ArrayType) {
            // i32*, [n x i32]*, [m x [n x i32]]*
            getElementPtrInstOperands.add(new IntegerConst(0));
            for (int i = 0; i < offsetDimension; i++) {
                if (currentBaseType instanceof ArrayType arrayType) {
                    getElementPtrInstOperands.add(dimOffsetValue[i]);
                    currentBaseType = arrayType.getBaseType();
                }
                else {
                    occurError = true;
                    ceHandler.visitorError(CEType.ov, "too many offsets", lVal.getIdentToken());
                    break;
                }
            }
        }

        // 访存模式
        if (memoryVisit) {
            if (symbol.isConst()) {
                occurError = true;
                ceHandler.visitorError(CEType.h, "cannot modified const value", lVal.getIdentToken());
            }
            if (!(currentBaseType instanceof IntegerType)) {
                occurError = true;
                ceHandler.visitorError(CEType.ov, "too less offsets", lVal.getIdentToken());
                return null;
            }
            resultValue = irBuildFactory.tryBuildGetElementPtrInst(resultValue, getElementPtrInstOperands);
        }
        // 取值模式
        else {
            if (currentBaseType instanceof IntegerType) {
                resultValue = irBuildFactory.tryBuildGetElementPtrInst(resultValue, getElementPtrInstOperands);
                resultValue = irBuildFactory.buildInst(new LoadInst(resultValue));
            }
            else if (currentBaseType instanceof PointerType) {
                resultValue = irBuildFactory.buildInst(new LoadInst(resultValue));
            }
            else {
                getElementPtrInstOperands.add(new IntegerConst(0));
                resultValue = irBuildFactory.tryBuildGetElementPtrInst(resultValue, getElementPtrInstOperands);
            }
        }

        return resultValue;
    }

    private Integer calcLVal(LVal lVal) {
        // 左值表达式   LVal → Ident {'[' Exp ']'} // c k

        // 处理维数
        int dimension = lVal.getDimension();
        int[] offsets = new int[dimension];
        Exp[] dimOffsetExp = lVal.getDimOffsetExp();
        boolean hasError = false;
        for (int i = 0; i < dimension; i++) {
            Integer temp = calcExp(dimOffsetExp[i]);
            if (temp == null) {
                hasError = true;
                continue;
            }
            offsets[i] = temp;
        }
        if (hasError) {
            return null;
        }

        // 查表获取symbol
        String identName = lVal.getIdentName();
        Symbol symbol = symbolTbl.findSymbol(identName);
        if (symbol == null) {
            ceHandler.visitorError(CEType.c, "ident not defined", lVal.getIdentToken(), lVal);
            return null;
        }

        // 非常量报错
        Integer result;
        Wrapper<Symbol.ErrorCase> upErrorCase = new Wrapper<>();
        if (!(symbol.isConst() || symbolTbl.isGlobalScope() && symbol.isGlobal())) {
            ceHandler.visitorError(CEType.h, "meeting inconstant value when constant calculating", lVal.getIdentToken(), lVal);
            return null;
        }

        // 计算
        result = symbol.getIntVal(upErrorCase, offsets);
        switch (upErrorCase.get()) {
            case SUCCESS -> {
                return result;
            }
            case OFFSETS_TOO_LONG -> {
                ceHandler.visitorError(CEType.i, "offsets too long", lVal.getIdentToken(), lVal);
                return null;
            }
            case OFFSETS_TOO_SHORT -> {
                ceHandler.visitorError(CEType.i, "offsets too short", lVal.getIdentToken(), lVal);
                return null;
            }
            case CROSS_THE_BORDER -> {
                ceHandler.visitorError(CEType.ov, "cross the border", lVal.getIdentToken(), lVal);
                return null;
            }
            default -> {
                return null;
            }
        }
    }
    private Value visitExp(Exp exp) {
        Value resultValue = visitAddExp(exp.getAddExp());
        return resultValue == null ? new IntegerConst(0): resultValue;
    }
    private Integer calcExp(Exp exp) {
        AddExp addExp = exp.getAddExp();
        return calcAddExp(addExp);
    }
    private Integer calcConstExp(ConstExp constExp) {
        AddExp addExp = constExp.getAddExp();
        return calcAddExp(addExp);
    }

    private void visitCond(Cond cond, BasicBlock trueBasicBlock, BasicBlock falseBasicBlock) {
        LOrExp lOrExp = cond.getlOrExp();
        visitLOrExp(lOrExp, trueBasicBlock, falseBasicBlock);
    }

    private Integer calcCond(Cond cond) {
        LOrExp lOrExp = cond.getlOrExp();
        return calcLOrExp(lOrExp);
    }

    private Value visitPrimaryExp(PrimaryExp primaryExp) {
        // 基本表达式   PrimaryExp → '(' Exp ')' | LVal | Number
        switch (primaryExp.getType()) {
            case PP_EXP -> {
                Exp exp = primaryExp.getExp();
                return visitExp(exp);
            }
            case LVAL -> {
                LVal lVal = primaryExp.getlVal();
                return visitLVal(lVal, false);
            }
            case NUMBER -> {
                int numberVal = primaryExp.getNumberVal();
                return new IntegerConst(numberVal);
            }
            default -> {
                throw new VisitInnerException();
            }
        }
    }

    private Integer calcPrimaryExp(PrimaryExp primaryExp) {
        switch (primaryExp.getType()) {
            case PP_EXP -> {
                return calcExp(primaryExp.getExp());
            }
            case LVAL -> {
                return calcLVal(primaryExp.getlVal());
            }
            case NUMBER -> {
                return primaryExp.getNumberVal();
            }
            default -> {
                throw new VisitInnerException();
            }
        }
    }

    private Value visitUnaryExp(UnaryExp unaryExp) {
        // 一元表达式   UnaryExp → PrimaryExp | Ident '(' [FuncRParams] ')' | UnaryOp UnaryExp // c d e j
        Value resultValue;
        switch (unaryExp.getType()) {
            case PRIMARY_EXP -> {
                PrimaryExp primaryExp = unaryExp.getPrimaryExp();
                resultValue = visitPrimaryExp(primaryExp);
            }
            case FUNCTION_CALL -> {
                // 查函数符号表
                String funcIdent = unaryExp.getFuncIdentName();
                FunctionIr functionIr = symbolTbl.findFunctionSymbol(funcIdent);
                if (functionIr == null) {
                    ceHandler.visitorError(CEType.c, "function not defined", unaryExp.getFuncIdentToken(), unaryExp);
                    return null;
                }

                // 构建形参Value Ir
                ArrayList<Exp> funcRParamsExps = unaryExp.getFuncRParamsExpList();
                ArrayList<Value> argValues = new ArrayList<>();
                for (Exp funcRParamsExp: funcRParamsExps) {
                    Value tempArgValue = visitExp(funcRParamsExp);
                    if (tempArgValue == null) {
                        return null;
                    }
                    argValues.add(tempArgValue);
                }

                // 检验
                // 参数数量检验
                ArrayList<Argument> arguments = functionIr.getArguments();
                int rParamsCnt = argValues.size();
                int fParamsCnt = arguments.size();
                if (rParamsCnt != fParamsCnt) {
                    ceHandler.visitorError(
                            CEType.d,
                            String.format("expected %d params, given %d", fParamsCnt, rParamsCnt),
                            unaryExp.getFuncIdentToken(), unaryExp
                    );
                    return null;
                }
                // 参数类型检验
                for (int i = 0; i < rParamsCnt; i++) {
                    Value rParam = argValues.get(i);
                    Argument fParam = arguments.get(i);
                    DataType rParamDataType = (DataType) rParam.getType();
                    DataType fParamDataType = fParam.getDataType();
                    if (!DataType.match(rParamDataType, fParamDataType)) {
                        ceHandler.visitorError(CEType.e, "param type unmatched",  unaryExp.getFuncIdentToken(), unaryExp);
                    }
                }

                // 生成函数调用Ir命令
                resultValue = irBuildFactory.buildInst(new CallInst(functionIr, argValues));

            }
            default -> {
                throw new VisitInnerException();
            }
        }

        if (resultValue == null) {
            return null;
        }

        for (TokenType operator: unaryExp.getOperators()) {
            switch (operator) {
                case MINU -> {
                    if (resultValue instanceof IntegerConst integerConst) {
                        resultValue = new IntegerConst(-integerConst.getValue());
                    }
                    else if (resultValue.getType() instanceof IntegerType integerType) {
                        if (integerType.isI1()) {
                            resultValue = irBuildFactory.buildInst(new ZextInst(resultValue));
                        }
                        resultValue = irBuildFactory.buildInst(new SubInst(new IntegerConst(0), resultValue));
                    }
                    else {
                        return null;
                    }
                }
                case PLUS -> {}
                case NOT -> {
                    if (resultValue instanceof IntegerConst integerConst) {
                        resultValue = new IntegerConst(integerConst.getValue() == 0 ? 1 : 0);
                    }
                    else if (resultValue.getType() instanceof IntegerType integerType) {
                        if (!integerType.isI1()) {
                            resultValue = irBuildFactory.buildInst(new IcmpInst(resultValue));
                        }
                        resultValue = irBuildFactory.buildInst(new XorInst(resultValue));
                    }
                    else {
                        return null;
                    }
                }
            }
        }

        return resultValue;
    }

    private Integer calcUnaryExp(UnaryExp unaryExp) {
        // 一元表达式   UnaryExp → PrimaryExp | Ident '(' [FuncRParams] ')' | UnaryOp UnaryExp // c d e j
        Integer result = null;
        switch (unaryExp.getType()) {
            case PRIMARY_EXP -> {
                PrimaryExp primaryExp = unaryExp.getPrimaryExp();
                result = calcPrimaryExp(primaryExp);
            }
            case FUNCTION_CALL -> {
                ceHandler.visitorError(CEType.ov, "constant calculation cannot include function calls", unaryExp);
            }
        }

        if (result == null) {
            return null;
        }

        for (TokenType operator: unaryExp.getOperators()) {
            switch (operator) {
                case PLUS -> {}
                case MINU -> {
                    result = - result;
                }
                case NOT -> {
                    result = (result == 0) ? 1 : 0;
                }
            }
        }

        return result;
    }

    private<T extends BinaryExpNode<C>, C extends Node> Integer calcBinaryExp(
            T t,
            Function<C, Integer> calcChildExp,
            TernaryFunction<Integer, TokenType, Integer, Integer> calc
    ) {
        Integer result = calcChildExp.apply(t.getHeadChildExp());
        for (Pair<TokenType, C> operation: t.getOperations()) {
            TokenType operator = operation.getFirst();
            C childExp = operation.getSecond();
            Integer temp = calcChildExp.apply(childExp);
            if (temp == null) result = null;
            if (result == null) continue;
            result = calc.apply(result, operator, calcChildExp.apply(childExp));
        }
        return result;
    }

    private Value formatI32(Value value, Node node) {
        if (value == null) return null;
        if (value.getType() instanceof IntegerType integerType) {
            if (!integerType.isI32()) {
                value = irBuildFactory.buildInst(new ZextInst(value));
            }
        }
        else {
            ceHandler.visitorError(CEType.ov, "non IntegerType Value cannot participate in operations", node);
            value = null;
        }
        return value;
    }

    private Value formatI1(Value value, Node node) {
        if (value == null) return null;
        if (value.getType() instanceof IntegerType integerType) {
            if (!integerType.isI1()) {
                value = irBuildFactory.buildInst(new IcmpInst(value));
            }
        }
        else {
            ceHandler.visitorError(CEType.ov, "non IntegerType Value cannot participate in operations", node);
            value = null;
        }
        return value;
    }


    private Value visitMulExp(MulExp mulExp) {
        UnaryExp headChildExp = mulExp.getHeadChildExp();
        Value resultValue = visitUnaryExp(headChildExp);
        if (mulExp.hasOperation()) {
            resultValue = formatI32(resultValue, headChildExp);
        }

        for (Pair<TokenType, UnaryExp> operation: mulExp.getOperations()) {
            TokenType operator = operation.getFirst();
            UnaryExp childExp = operation.getSecond();
            Value tempValue = visitUnaryExp(childExp);
            tempValue = formatI32(tempValue, childExp);
            if (tempValue == null) resultValue = null;
            if (resultValue == null) continue;
            switch (operator) {
                case MULT -> resultValue = irBuildFactory.buildInst(new MulInst(resultValue, tempValue));
                case DIV -> resultValue = irBuildFactory.buildInst(new SdivInst(resultValue, tempValue));
                case MOD -> resultValue = irBuildFactory.buildInst(new SremInst(resultValue, tempValue));
            }
        }
        return resultValue;
    }

    private Integer calcMulExp(MulExp mulExp) {
        return calcBinaryExp(mulExp, this::calcUnaryExp,
                (origin, operator, operand) -> {
                    switch (operator) {
                        case MULT -> {
                            return origin * operand;
                        }
                        case DIV -> {
                            if (operand == 0) {
                                ceHandler.visitorError(CEType.ov, "divide by zero when constant calculating", mulExp);
                                return null;
                            }
                            return origin / operand;
                        }
                        case MOD -> {
                            if (operand == 0) {
                                ceHandler.visitorError(CEType.ov, "divide by zero when constant calculating", mulExp);
                                return null;
                            }
                            return origin % operand;
                        }
                    }
                    return null;
                }
        );
    }

    private Value visitAddExp(AddExp addExp) {
        MulExp headChildExp = addExp.getHeadChildExp();
        Value resultValue = visitMulExp(headChildExp);
        if (addExp.hasOperation()) {
            resultValue = formatI32(resultValue, headChildExp);
        }

        for (Pair<TokenType, MulExp> operation: addExp.getOperations()) {
            TokenType operator = operation.getFirst();
            MulExp childExp = operation.getSecond();
            Value tempValue = visitMulExp(childExp);
            tempValue = formatI32(tempValue, childExp);
            if (tempValue == null) resultValue = null;
            if (resultValue == null) continue;
            switch (operator) {
                case PLUS -> resultValue = irBuildFactory.buildInst(new AddInst(resultValue, tempValue));
                case MINU -> resultValue = irBuildFactory.buildInst(new SubInst(resultValue, tempValue));
            }
        }
        return resultValue;
    }

    private Integer calcAddExp(AddExp addExp) {
        return calcBinaryExp(addExp, this::calcMulExp,
                (origin, operator, operand) -> {
                    switch (operator) {
                        case PLUS -> {
                            return origin + operand;
                        }
                        case MINU -> {
                            return origin - operand;
                        }
                    }
                    return null;
                }
        );
    }

    private Value visitRelExp(RelExp relExp) {
        AddExp headChildExp = relExp.getHeadChildExp();
        Value resultValue = visitAddExp(headChildExp);

        for (Pair<TokenType, AddExp> operation: relExp.getOperations()) {
            TokenType operator = operation.getFirst();
            AddExp childExp = operation.getSecond();
            resultValue = formatI32(resultValue, headChildExp);
            Value tempValue = visitAddExp(childExp);
            tempValue = formatI32(tempValue, childExp);
            if (tempValue == null) resultValue = null;
            if (resultValue == null) continue;
            switch (operator) {
                case LSS -> resultValue = irBuildFactory.buildInst(new IcmpInst(IcmpInst.IcmpCondition.slt, resultValue, tempValue));
                case LEQ -> resultValue = irBuildFactory.buildInst(new IcmpInst(IcmpInst.IcmpCondition.sle, resultValue, tempValue));
                case GRE -> resultValue = irBuildFactory.buildInst(new IcmpInst(IcmpInst.IcmpCondition.sgt, resultValue, tempValue));
                case GEQ -> resultValue = irBuildFactory.buildInst(new IcmpInst(IcmpInst.IcmpCondition.sge, resultValue, tempValue));
            }
        }
        return resultValue;
    }

    private Integer calcRelExp(RelExp relExp) {
        return calcBinaryExp(relExp, this::calcAddExp,
                (origin, operator, operand) -> {
                    switch (operator) {
                        case LSS -> {
                            return origin < operand ? 1 : 0;
                        }
                        case LEQ -> {
                            return origin <= operand ? 1 : 0;
                        }
                        case GRE -> {
                            return origin > operand ? 1 : 0;
                        }
                        case GEQ -> {
                            return origin >= operand ? 1 : 0;
                        }
                    }
                    return null;
                }
        );
    }

    private Value visitEqExp(EqExp eqExp) {
        RelExp headChildExp = eqExp.getHeadChildExp();
        Value resultValue = visitRelExp(headChildExp);

        for (Pair<TokenType, RelExp> operation: eqExp.getOperations()) {
            TokenType operator = operation.getFirst();
            RelExp childExp = operation.getSecond();
            resultValue = formatI32(resultValue, headChildExp);
            Value tempValue = visitRelExp(childExp);
            tempValue = formatI32(tempValue, childExp);
            if (tempValue == null) resultValue = null;
            if (resultValue == null) continue;
            switch (operator) {
                case EQL -> resultValue = irBuildFactory.buildInst(new IcmpInst(IcmpInst.IcmpCondition.eq, resultValue, tempValue));
                case NEQ -> resultValue = irBuildFactory.buildInst(new IcmpInst(IcmpInst.IcmpCondition.ne, resultValue, tempValue));
            }
        }
        return resultValue;
    }

    private Integer calcEqExp(EqExp eqExp) {
        return calcBinaryExp(eqExp, this::calcRelExp,
                (origin, operator, operand) -> {
                    switch (operator) {
                        case EQL -> {
                            return origin == operand ? 1 : 0;
                        }
                        case NEQ -> {
                            return origin != operand ? 1 : 0;
                        }
                    }
                    return null;
                }
        );
    }

    private void visitMiddleLayer(EqExp eqExp, BasicBlock trueBasicBlock, BasicBlock falseBasicBlock) {
        Value resultValue = visitEqExp(eqExp);
        resultValue = formatI1(resultValue, eqExp);
        irBuildFactory.buildInst(new ConditionalBrInst(resultValue, trueBasicBlock, falseBasicBlock));
    }
    private void visitLAndExp(LAndExp lAndExp, BasicBlock trueBasicBlock, BasicBlock falseBasicBlock) {
        EqExp curChildExp = lAndExp.getHeadChildExp();
        for (Pair<TokenType, EqExp> operation: lAndExp.getOperations()) {
            BBList<BasicBlock>.BBNode newBasicBlockNode = irBuildFactory.createBasicBlock();
            visitMiddleLayer(curChildExp, newBasicBlockNode.get(), falseBasicBlock);
            irBuildFactory.switchCurBasicBlock(newBasicBlockNode);
            curChildExp = operation.getSecond();
        }
        visitMiddleLayer(curChildExp, trueBasicBlock, falseBasicBlock);
    }

    private Integer calcLAndExp(LAndExp lAndExp) {
        return calcBinaryExp(lAndExp, this::calcEqExp,
                (origin, operator, operand) -> {
                    if (operator == TokenType.AND) {
                        return origin == 1 && operand == 1 ? 1 : 0;
                    }
                    return null;
                }
        );
    }

    private void visitLOrExp(LOrExp lOrExp, BasicBlock trueBasicBlock, BasicBlock falseBasicBlock) {
        LAndExp curChildExp = lOrExp.getHeadChildExp();
        for (Pair<TokenType, LAndExp> operation: lOrExp.getOperations()) {
            BBList<BasicBlock>.BBNode newBasicBlockNode = irBuildFactory.createBasicBlock();
            visitLAndExp(curChildExp, trueBasicBlock, newBasicBlockNode.get());
            irBuildFactory.switchCurBasicBlock(newBasicBlockNode);
            curChildExp = operation.getSecond();
        }
        visitLAndExp(curChildExp, trueBasicBlock, falseBasicBlock);
    }

    private Integer calcLOrExp(LOrExp lOrExp) {
        return calcBinaryExp(lOrExp, this::calcLAndExp,
                (origin, operator, operand) -> {
                    if (operator == TokenType.OR) {
                        return origin == 1 || operand == 1 ? 1 : 0;
                    }
                    return null;
                }
        );
    }


}
